{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению. Сессия № 2\n",
    "Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Тема 10. Бустинг\n",
    "## <center>Часть 3. Xgboost, стандартный Python-интерфейс"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Загрузка бибилиотек"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import xgboost as xgb\n",
    "from sklearn.metrics import accuracy_score, f1_score\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import LabelEncoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Загрузка и подготовка данных\n",
    "\n",
    "Посмотрим на примере данных по оттоку клиентов из телеком-компании.\n",
    "\n",
    "> **Важно**: XGBoost поддерживает только числовые признаки. Поэтому текстовые признаки требуют предобработки\n",
    "\n",
    "Для обучения с Xgboost данные должны быть представлены в виде объекта `DMatrix`, предназначенного для представления разреженных данных. Его можно инициализировать через:\n",
    "- текстовый формат libsvm,\n",
    "-  Numpy 2D array (чаще всего)\n",
    "- бинарный буфер-файл XGBoost "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Загрузим данные и осуществим минимальную предобработку.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv(\"../../data/telecom_churn.csv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Штаты просто занумеруем (хотя можно и лучше поступить), а признаки International plan (наличие международного роуминга), Voice mail plan (наличие голосовой почтыы) и целевой Churn сделаем бинарными.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "state_enc = LabelEncoder()\n",
    "df[\"State\"] = state_enc.fit_transform(df[\"State\"])\n",
    "df[\"International plan\"] = (df[\"International plan\"] == \"Yes\").astype(\"int\")\n",
    "df[\"Voice mail plan\"] = (df[\"Voice mail plan\"] == \"Yes\").astype(\"int\")\n",
    "df[\"Churn\"] = (df[\"Churn\"]).astype(\"int\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Разделим данные на обучающую и тестовую выборки в отношении 7:3. Инициализируем соотв. объекты DMatrix dtrain и dtest.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    df.drop(\"Churn\", axis=1),\n",
    "    df[\"Churn\"],\n",
    "    test_size=0.3,\n",
    "    stratify=df[\"Churn\"],\n",
    "    random_state=17,\n",
    ")\n",
    "dtrain = xgb.DMatrix(X_train, y_train)\n",
    "dtest = xgb.DMatrix(X_test, y_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Посмотрим на статистику полученных объектов:**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\n",
    "    \"Train dataset contains {0} rows and {1} columns\".format(\n",
    "        dtrain.num_row(), dtrain.num_col()\n",
    "    )\n",
    ")\n",
    "print(\n",
    "    \"Test dataset contains {0} rows and {1} columns\".format(\n",
    "        dtest.num_row(), dtest.num_col()\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Train mean target: \")\n",
    "print(np.mean(dtrain.get_label()))\n",
    "\n",
    "print(\"\\nTest mean target: \")\n",
    "print(np.mean(dtest.get_label()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Инициализация параметров\n",
    "\n",
    "- бинарная классификация (`'objective':'binary:logistic'`)\n",
    "- ограничим глубину деревьев (`'max_depth':3`)\n",
    "- не хотим лишнего вывода (`'silent':1`)\n",
    "- проведем 50 итераций бустинга\n",
    "- шаг градиентного спуска довольно большой (`'eta':1`) - алгоритм будет обучаться быстро и \"агрессивно\" (лучше результаты будут, если уменьшить eta и увеличить число итераций)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "params = {\"objective\": \"binary:logistic\", \"max_depth\": 3, \"silent\": 1, \"eta\": 1}\n",
    "\n",
    "num_rounds = 50"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Обучение классификатора\n",
    "Тут мы просто передаем слоавть параметров, данные и число итераций."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "xgb_model = xgb.train(params, dtrain, num_rounds)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**С помощью `watchlist` отслеживать качество алгоритма на тестовой выборке для каждой итерации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "watchlist = [(dtest, \"test\"), (dtrain, \"train\")]  # native interface only\n",
    "xgb_model = xgb.train(params, dtrain, num_rounds, watchlist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Прогнозы для тестовой выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_prob = xgb_model.predict(dtest)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Посчитаем долю правильных ответов алгоритма на тестовой выборке.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "predicted_labels = preds_prob > 0.5\n",
    "print(\n",
    "    \"Accuracy and F1 on the test set are: {} and {}\".format(\n",
    "        round(accuracy_score(y_test, predicted_labels), 3),\n",
    "        round(f1_score(y_test, predicted_labels), 3),\n",
    "    )\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
